//gcc spidev-testc.c -lspidev-lib -o spidev-testc编译

//如果上面编译不成功就用下面的
/*
# 进入 src 目录
cd ~/shu_mei_pai/shu_mei_pai_5/src

# 编译为静态库
gcc -c spidev_lib.c -o spidev_lib.o
ar rcs libspidev-lib.a spidev_lib.o

# 返回 sample 目录
cd ../sample

# 编译并链接
gcc spidev-testc.c -I../src -L../src -lspidev-lib -o spidev-testc
*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "spidev_lib.h"
#include <linux/spi/spidev.h>

// MCP23S17相关寄存器地址定义
#define IODIRA 0x00        // 端口A方向寄存器地址,当IODIRA寄存器的值为0x00时，端口 A 被配置为输出模式。
#define GPIOA  0x12        // 端口A数据寄存器地址
#define GPIOB  0x13        // 端口B数据寄存器地址
#define IODIRB 0x01        // 端口B方向寄存器地址
#define OUTPUT 0X00        // 输出模式
#define INPUT  0xFF        // 输入模式

// 用于控制GPB0 - GPB3电平的宏
#define GPB0_3_LEVEL 0x0F  // 这里0x0F表示高电平，若要低电平则改为0x00，修改这个宏即可改变电平

// MCP23S17设备地址（根据实际连接情况可能需要修改）
#define MCP23S17_ADDR 0x20

spi_config_t spi_config;
uint8_t tx_buffer[32];
uint8_t rx_buffer[32];

// 通过SPI向MCP23S17写一个字节数据到指定寄存器
void mcp23s17_write_register(int spifd, uint8_t reg, uint8_t value) {
    // 构造SPI传输数据，前3位为010（固定），接着是设备地址（A2A1A0 = 000），然后是写操作位（0），再接着是寄存器地址和要写入的值
    tx_buffer[0] = ((MCP23S17_ADDR << 1) | 0);
    tx_buffer[1] = reg;
    tx_buffer[2] = value;
    spi_xfer(spifd, tx_buffer, 3, rx_buffer, 3);
}

// 通过SPI从MCP23S17的指定寄存器读取一个字节数据
uint8_t mcp23s17_read_register(int spifd, uint8_t reg) {
    // 先写寄存器地址（用于设置要读的寄存器）
    tx_buffer[0] = ((MCP23S17_ADDR << 1) | 1);
    tx_buffer[1] = reg;
    // 然后发起读操作，前3位为010（固定），接着是设备地址（A2A1A0 = 000），然后是读操作位（1）
    tx_buffer[2] = 0;
    spi_xfer(spifd, tx_buffer, 3, rx_buffer, 3);
    return rx_buffer[2];
}

// 设置端口为输出模式
void set_port_output(int spifd, uint8_t port_reg) {
    mcp23s17_write_register(spifd, port_reg, OUTPUT);
}

// 设置端口为输入模式
void set_port_input(int spifd, uint8_t port_reg) {
    mcp23s17_write_register(spifd, port_reg, INPUT);
}

// 设置端口电平
void set_port_level(int spifd, uint8_t port_reg, uint8_t level) {
    mcp23s17_write_register(spifd, port_reg, level);
}

// 读取端口电平
uint8_t read_port_level(int spifd, uint8_t port_reg) {
    return mcp23s17_read_register(spifd, port_reg);
}

int main(void) {
    int spifd;
    spi_config.mode = 0;
    spi_config.speed = 1000000;  // 1000000Hz ,1000kHz, 1MHz
    spi_config.delay = 0;
    spi_config.bits_per_word = 8;

    spifd = spi_open("/dev/spidev1.0", spi_config);

    // 设置GPA为输入模式
    set_port_input(spifd, IODIRA);
    // 设置GPB为输出模式
    set_port_output(spifd, IODIRB);

    while(1)
    {
        // 设置GPB0 - GPB3为指定电平
        set_port_level(spifd, GPIOB, GPB0_3_LEVEL);

        // 读取GPA状态
        uint8_t gpa_state = read_port_level(spifd, GPIOA);

        // 读取GPB状态
        uint8_t gpb_state = read_port_level(spifd, GPIOB);

        printf("===================\r\n");

        printf("GPB0-3 set high\r\n");

        // 打印GPB0 - GPB3的状态
        for (int i = 0; i < 4; i++) 
        {
            printf("GPB%d Read: 0X%02X\n", i, (gpb_state >> i) & 0x01);
        }

        printf("---------\r\n");

        set_port_level(spifd, GPIOB, 0x00);

        // 再次读取GPA状态
        gpa_state = read_port_level(spifd, GPIOA);

        // 再次读取GPB状态
        gpb_state = read_port_level(spifd, GPIOB);

        printf("GPB0-3 set low\r\n");

        // 打印GPB0 - GPB3的状态
        for (int i = 0; i < 4; i++) 
        {
            printf("GPB%d Read: 0X%02X\n", i, (gpb_state >> i) & 0x01);
        }

        printf("---------\r\n");

        // 打印GPA0 - GPA7的状态
        for (int i = 0; i < 8; i++) 
        {
            printf("GPA%d Read = 0X%02X\n", i, (gpa_state >> i) & 0x01);
        }

        usleep(500000);
    }

    spi_close(spifd);

    return 0;
}